package com.gzdh.device.config;


import com.gzdh.device.comm.util.DefaultPasswordEncoder;
import com.gzdh.device.config.annotation.IgnoreAuthorize;
import com.gzdh.device.security.TokenAuthFilter;
import com.gzdh.device.security.TokenLoginFilter;
import com.gzdh.device.security.TokenLogoutHandler;
import com.gzdh.device.security.UnauthEntryPoint;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.Resource;
import java.util.Map;

/**
 * SecurityConfig
 */
@Slf4j
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {

    private TokenManager tokenManager;
    private RedisTemplate redisTemplate;
    //自定义密码加密器
    private DefaultPasswordEncoder defaultPasswordEncoder;
    private UserDetailsService userDetailsService;

    @Autowired
    private UnauthEntryPoint unauthEntryPoint;
    @Autowired
    private  RequestMappingHandlerMapping requestMappingHandlerMapping;
    @Autowired
    public TokenWebSecurityConfig(UserDetailsService userDetailsService, DefaultPasswordEncoder defaultPasswordEncoder,
                                  TokenManager tokenManager, RedisTemplate redisTemplate) {
        this.userDetailsService = userDetailsService;
        this.defaultPasswordEncoder = defaultPasswordEncoder;
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;
    }

    /**
     * 配置设置
     * 设置退出的地址和token，redis操作地址
     *
     * @param http
     * @throws Exception
     */

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling()
                //没有权限访问 执行 自定义的 未授权统一处理器 UnauthEntryPoint()

                .and().csrf().disable()
                .authorizeRequests()
                .antMatchers("/error").permitAll()
                .anyRequest().authenticated()
                //退出路径
                .and().logout().logoutUrl("/admin/acl/index/logout")
                //退出后执行
                .addLogoutHandler(new TokenLogoutHandler(tokenManager, redisTemplate)).and()
                //添加自定义过滤器
                .addFilter(new TokenLoginFilter(authenticationManager(), tokenManager, redisTemplate))
                .addFilter(new TokenAuthFilter(authenticationManager(), tokenManager, redisTemplate)).httpBasic()
                .authenticationEntryPoint(unauthEntryPoint)
                .and().sessionManagement().maximumSessions(1);
    }

    /**
     * 调用userDetailsService和密码处理
     *
     * @param auth
     * @throws Exception
     */
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
        System.out.println("初始密码：" + passwordEncoder().encode("Abc@2023"));
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
//    /**
//     * 不进行认证的路径，可以直接访问
//     * @param web
//     * @throws Exception
//     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/api/thir/**");
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> handlerMethodEntry : handlerMethods.entrySet()) {
            RequestMappingInfo key = handlerMethodEntry.getKey();
            HandlerMethod value = handlerMethodEntry.getValue();
            IgnoreAuthorize annotation = value.getMethodAnnotation(IgnoreAuthorize.class);
            log.info("[key] - [{}] - [value] - [{}]", key, value);
            if (annotation != null && annotation.web()) {
                key.getPatternsCondition().getPatterns().forEach(url -> web.ignoring().antMatchers(url));
            }
        }
    }
}
